package cn.qidd.keep.manage.service.system.impl;

import cn.qidd.keep.domain.web.BusinessException;
import cn.qidd.keep.manage.model.system.JobModel;
import cn.qidd.keep.manage.model.system.JobParams;
import cn.qidd.keep.manage.model.system.TriggerModel;
import cn.qidd.keep.manage.model.system.TriggerParams;
import cn.qidd.keep.manage.service.system.TaskService;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * Created by sunyanping on 2018/10/31
 */
@Transactional(rollbackOn = Exception.class)
@Service
public class TaskServiceImpl implements TaskService {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @Override
    public List<JobModel> findAllJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        GroupMatcher<JobKey> jobKeyGroupMatcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(jobKeyGroupMatcher);
        List<JobModel> modelList = Lists.newArrayList();
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            Date pervFireTime = null;
            Date nextFireTime = null;
            List<String> triggerStateList = new ArrayList<>();
            for (Trigger trigger : triggers) {
                if (!(trigger instanceof CronTrigger)) {
                    continue;
                }
                CronTriggerImpl cronTrigger = (CronTriggerImpl) trigger;
                Date previousFireTime = cronTrigger.getPreviousFireTime();
                if (pervFireTime == null) {
                    pervFireTime = previousFireTime;
                } else {
                    if (previousFireTime != null && previousFireTime.after(pervFireTime)) {
                        pervFireTime = previousFireTime;
                    }
                }
                Date nextFireTime1 = cronTrigger.getNextFireTime();
                if (nextFireTime == null) {
                    nextFireTime = nextFireTime1;
                } else {
                    if (nextFireTime.after(nextFireTime1)) {
                        nextFireTime = nextFireTime1;
                    }
                }
                Trigger.TriggerState triggerState = scheduler.getTriggerState(cronTrigger.getKey());
                triggerStateList.add(triggerState.name());
            }
            String jobState = Trigger.TriggerState.NONE.name();
            if (triggerStateList.contains(Trigger.TriggerState.ERROR.name())) {
                jobState = Trigger.TriggerState.ERROR.name();
            } else if (triggerStateList.contains(Trigger.TriggerState.BLOCKED.name())) {
                jobState = Trigger.TriggerState.BLOCKED.name();
            } else if (triggerStateList.stream()
                    .allMatch(e -> e.equals(Trigger.TriggerState.COMPLETE.name()))) {
                jobState = Trigger.TriggerState.COMPLETE.name();
            } else if (triggerStateList.stream()
                    .allMatch(e -> e.equals(Trigger.TriggerState.PAUSED.name()))) {
                jobState = Trigger.TriggerState.PAUSED.name();
            } else if (triggerStateList.contains(Trigger.TriggerState.NORMAL.name())) {
                jobState = Trigger.TriggerState.NORMAL.name();
            }
            JobModel model = new JobModel();
            model.setJobName(jobKey.getName());
            model.setJobGroup(jobKey.getGroup());
            model.setJobClassName(jobKey.getClass().getSimpleName());
            model.setDescription(scheduler.getJobDetail(jobKey).getDescription());
            model.setPrevFireTime(pervFireTime);
            model.setNextFireTime(nextFireTime);
            model.setJobStatus(jobState);
            modelList.add(model);
        }
        return modelList;
    }

    @Override
    public void updateJob(JobParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        JobDetail jobDetail = scheduler.getJobDetail(jobKey).getJobBuilder()
                .withDescription(params.getDescription()).build();
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (isRun(scheduler, triggers)) throw new BusinessException("任务正在执行,请稍后再操作");
        scheduler.scheduleJob(jobDetail, Sets.newHashSet(triggers), true);
    }

    @Override
    public void pauseJob(JobParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        scheduler.pauseJob(jobKey);
    }

    @Override
    public void resumeJob(JobParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        scheduler.resumeJob(jobKey);
    }

    @Override
    public void deleteJob(JobParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (isRun(scheduler, triggers)) throw new BusinessException("任务正在执行,请稍后再操作");
        scheduler.deleteJob(jobKey);
    }

    @Override
    public void runJob(JobParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (isRun(scheduler, triggers)) throw new BusinessException("任务正在执行,请稍后再操作");
        scheduler.triggerJob(jobKey);
    }

    private boolean isRun(Scheduler scheduler, List<? extends Trigger> triggers) {
        return triggers.stream().anyMatch(e -> {
            try {
                return scheduler.getTriggerState(e.getKey()) == Trigger.TriggerState.BLOCKED;
            } catch (SchedulerException e1) {
                e1.printStackTrace();
                return true;
            }
        });
    }

    @Override
    public List<TriggerModel> getAllTriggerOfJob(String jobName, String jobGroup) throws SchedulerException, BusinessException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
        List<TriggerModel> triggerModelList = new ArrayList<>();
        for (Trigger trigger : triggersOfJob) {
            if (!(trigger instanceof CronTrigger)) {
                continue;
            }
            CronTriggerImpl cronTrigger = (CronTriggerImpl) trigger;
            TriggerModel triggerModel = new TriggerModel();
            triggerModel.setJobName(cronTrigger.getJobName());
            triggerModel.setJobGroup(cronTrigger.getJobGroup());
            triggerModel.setCronExpression(cronTrigger.getCronExpression());
            triggerModel.setTriggerName(cronTrigger.getName());
            triggerModel.setTriggerGroup(cronTrigger.getGroup());
            triggerModel.setDescription(cronTrigger.getDescription());
            triggerModel.setPrevFireTime(cronTrigger.getPreviousFireTime());
            triggerModel.setNextFireTime(cronTrigger.getNextFireTime());
            triggerModel.setTriggerState(scheduler.getTriggerState(cronTrigger.getKey()));
            triggerModelList.add(triggerModel);
        }
        return triggerModelList;
    }

    @Override
    public void addTrigger(TriggerParams params) throws BusinessException, SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        if (!scheduler.checkExists(jobKey)) throw new BusinessException("任务不存在");
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (isRun(scheduler, triggers)) throw new BusinessException("任务正在执行,请稍后再操作");
        TriggerKey triggerKey = TriggerKey.triggerKey(params.getTriggerName());
        if (scheduler.checkExists(triggerKey)) throw new BusinessException("触发器名称已存在");
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (!CronExpression.isValidExpression(params.getCronExpression())) throw new BusinessException("CRON表达式有误");
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(params.getCronExpression());
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(params.getTriggerName())
                .withSchedule(scheduleBuilder)
                .forJob(jobDetail)
                .build();
        scheduler.scheduleJob(trigger);
    }

    @Override
    public void deleteTrigger(TriggerParams params) throws BusinessException, SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(params.getJobName(), params.getJobGroup());
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (isRun(scheduler, triggers)) throw new BusinessException("任务正在执行,请稍后再操作");
        TriggerKey triggerKey = TriggerKey.triggerKey(params.getTriggerName());
        scheduler.unscheduleJob(triggerKey);
    }

    @Override
    public void pauseTrigger(TriggerParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(params.getTriggerName());
        scheduler.pauseTrigger(triggerKey);
    }

    @Override
    public void resumeTrigger(TriggerParams params) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(params.getTriggerName());
        scheduler.resumeTrigger(triggerKey);
    }
}
